package cxm.lib.speex;

public class Preprocessor {
	static {
		System.loadLibrary("speex_jni"); // to load the libwebrtc_aecm.so library.
    }
	
	private int preprocessorState = 0;
	private int frameSize = 0;
	
	public Preprocessor(int frameSize, int sampleRate) throws SpeexException {
		this.preprocessorState = speexStateInit(frameSize, sampleRate);
		if (this.preprocessorState <= 0)
			throw new SpeexException("Cannot init");
		this.frameSize = frameSize;
	}
	
	public void close() {
		if (this.preprocessorState <= 0)
			return;
		speexStateDestroy(this.preprocessorState);
		this.preprocessorState = 0;
	}
	
	public void run(short []buffer) {
		if (this.frameSize != buffer.length)
			throw new IllegalArgumentException("Buffer length mismatch");
		
		speexRun(this.preprocessorState, buffer);
	}
	
	public void enableEchoCancel(EchoCancellation echoCancel) throws SpeexException {
		if (null == echoCancel)
			throw new IllegalArgumentException("Null echo state");
		
		Integer value = echoCancel.getState();
		int ret = speexControl(this.preprocessorState,
				SPEEX_PREPROCESS_SET_ECHO_STATE, value);
		if (0 != ret)
			throw new SpeexException("Failed: " + ret);
	}
	
	// give a negative value
	// nose suppress default enable with vaule -15
	public void setNoiseSuppressLevel(int level) throws SpeexException {
		if (level >= 0)
			throw new IllegalArgumentException("level");
		
		Integer value = level;
		int ret = speexControl(this.preprocessorState,
				SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, value);
		if (0 != ret)
			throw new SpeexException("Failed: " + ret);
	}
	
	// enable / disable Automatic Gain Control(AGC)
	public void enableGain(boolean isEnable) throws SpeexException {
		Integer value = 0;
		if (isEnable)
			value = 1;
		
		int ret = speexControl(this.preprocessorState,
				SPEEX_PREPROCESS_SET_AGC, value);
		if (0 != ret)
			throw new SpeexException("Failed: " + ret);
	}
	
	// set AGC level, with default value 8000
	// range from 0 to 32768
	public void setGainLevel(int level) throws SpeexException {
		if (level < 0 || level > 32768)
			throw new IllegalArgumentException("level");
		
		Integer value = level;
		int ret = speexControl(this.preprocessorState,
				SPEEX_PREPROCESS_SET_AGC_LEVEL, value);
		if (0 != ret)
			throw new SpeexException("Failed: " + ret);
	}
	
	@Override
	protected void finalize() throws Throwable {
		super.finalize();
		this.close();
	}
	
	/* Set maximum attenuation of the residual echo in dB when near end is active (negative number) */
	private static final int SPEEX_PREPROCESS_SET_ECHO_STATE = 24;
	private static final int SPEEX_PREPROCESS_SET_NOISE_SUPPRESS = 18;
	private static final int SPEEX_PREPROCESS_SET_AGC = 2;
	private static final int SPEEX_PREPROCESS_SET_AGC_LEVEL = 6;
	
	/* Creates a new preprocessing state. You MUST create one state per channel processed.
	 * @param frame_size Number of samples to process at one time (should correspond to 10-20 ms). Must be
	 * the same value as that used for the echo canceller for residual echo cancellation to work.
	 * @param sampling_rate Sampling rate used for the input.    
	 * @return Newly created preprocessor state    
	*/
	private native int speexStateInit(int frameSize, int samplingRate);
	/* Destroys a preprocessor state     
	 * @param st Preprocessor state to destroy    
	*/
	private native void speexStateDestroy(int speexState);
	/* Preprocess a frame 
	 * @param st Preprocessor state
	 * @param x Audio sample vector (in and out). Must be same size as specified in speex_preprocess_state_init().
	 * @return Bool value for voice activity (1 for speech, 0 for noise/silence), ONLY if VAD turned on.
	*/
	private native int speexRun(int speexState, short []buffer);
	/* Used like the ioctl function to control the preprocessor parameters 
	 * @param st Preprocessor state
	 * @param request ioctl-type request (one of the SPEEX_PREPROCESS_* macros)
	 * @param ptr Data exchanged to-from function
	 * @return 0 if no error, -1 if request in unknown
	*/
	private native int speexControl(int speexState, int request, Object param);
}
